package uk.co.agileworks.bidpirate.service;

import java.util.ArrayList;
import java.util.List;

import javax.annotation.Resource;

import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import uk.co.agileworks.bidpirate.model.Bid;
import uk.co.agileworks.bidpirate.model.Item;
import uk.co.agileworks.bidpirate.model.Status;
import uk.co.agileworks.bidpirate.model.User;
import uk.co.agileworks.bidpirate.web.UserSession;

// TODO @Transactional?
@Service
public final class BusinessLogicImpl implements BusinessLogic {

	private static final Logger LOGGER = LoggerFactory.getLogger(BusinessLogicImpl.class);

	@Autowired
	private DAO dao;

	@Autowired
	private EbayFacade ebayFacade;

	@Autowired
	private UserSession userSession;

	@Resource
	private Integer minsBeforeBidding;

	@Override
	public void addBid(Bid bid) {
		bid.setStatus(userSession.isLoggedIn() ? Status.ACTIVE : Status.UNSAVED);
		bid.resetWatch();
		dao.saveOrUpdateBid(bid);
		LOGGER.info("Added bid. {" + bid + "}");
	}

	@Override
	public void deleteBid(long id) {
		Bid bid = dao.getBid(id);
		assertUser(bid);
		assertNotBidding(bid);
		dao.deleteBid(bid);
		LOGGER.info("Deleted bid. {" + bid + "}");
	}

	@Override
	public void editBid(Bid bid) {
		assertNotBidding(bid);
		bid.resetWatch();
		dao.saveOrUpdateBid(bid);
		LOGGER.info("Edited bid. {" + bid + "}");
	}

	@Override
	public void endBids() {
		List<Bid> bids = dao.getBidsBefore(new DateTime(), Status.WATCH);
		for (Bid bid : bids) {
			bid.setStatus(Status.END);
			updateItem(bid);
			dao.saveOrUpdateBid(bid);
			LOGGER.info("End watch. {" + bid + "}");
		}
	}

	@Override
	public Bid getBid(long id) {
		Bid bid = dao.getBid(id);
		assertUser(bid);
		return dao.getBid(id);
	}

	@Override
	public List<Bid> getBidding() {
		DateTime biddingTime = new DateTime().plusMinutes(minsBeforeBidding);
		List<Bid> bids = dao.getBidsBefore(biddingTime, Status.ACTIVE);
		for (Bid bid : bids) {
			bid.setStatus(Status.BIDDING);
			dao.saveOrUpdateBid(bid);
		}
		return bids;
	}

	@Override
	public List<Bid> listBids(User user) {
		List<Bid> bids = new ArrayList<Bid>();
		if (user.getId() != null) {
			bids = dao.getBids(user);
		}
		return bids;
	}

	@Override
	public boolean logIn(User userObj) {
		LOGGER.debug("userObj=" + userObj);
		EbaySession ebaySession = ebayFacade.newEbaySession();
		boolean signedIn = ebaySession.signIn(userObj);
		if (signedIn) {
			User sessionUser = userSession.getUser();
			LOGGER.debug("sessionUser=" + sessionUser);
			User persistedUser = dao.getUser(userObj.getUsername());
			LOGGER.debug("persistedUser=" + persistedUser);
			if (persistedUser == null) { // create bidpirate user
				userObj.setId(sessionUser.getId());
				dao.saveOrUpdateUser(userObj);
				persistedUser = userObj;
			} else { // ebay password can change
				persistedUser.setPassword(userObj.getPassword());
				dao.saveOrUpdateUser(persistedUser);
			}
			if (sessionUser.getId() != null) { // activate bids in current session
				for (Bid bid : dao.getBids(sessionUser)) {
					bid.setStatus(Status.ACTIVE);
					bid.resetWatch();
					bid.setUser(persistedUser);
					dao.saveOrUpdateBid(bid);
				}
			}
			persistedUser.setRegion(userObj.getRegion());
			userSession.logIn(persistedUser);
			ebaySession.signOut();
		}
		return signedIn;
	}

	@Override
	public void stopGroup(Bid thisBid) {
		if (thisBid.isInGroup()) {
			for (Bid bid : dao.getBids(thisBid.getUser())) {
				if (bid.getGroup().equals(thisBid.getGroup()) && bid.getStatus() == Status.ACTIVE) {
					bid.setStatus(Status.WATCH);
					dao.saveOrUpdateBid(bid);
					LOGGER.info("Stop group. {" + bid + "}");
				}
			}
		}
	}

	@Override
	public void updateItem(long bidId) {
		Bid bid = dao.getBid(bidId);
		updateItem(bid);
		dao.saveOrUpdateBid(bid);
		LOGGER.info("Updated item. {" + bid.getItem() + "}");
	}

	private void assertNotBidding(Bid bid) {
		if (Status.BIDDING == bid.getStatus()) {
			throw new ServiceException("currently.bidding");
		}
	}

	private void assertUser(Bid bid) {
		if (!bid.getUser().getId().equals(userSession.getUser().getId())) {
			throw new IllegalArgumentException();
		}
	}

	private void updateItem(Bid bid) {
		String itemUrl = ebayFacade.getItemUrl(bid.getItem().getItemNumber());
		Item item = ebayFacade.parseItem(itemUrl);
		bid.getItem().setEndTime(item.getEndTime());
		bid.getItem().setPrice(item.getPrice());
	}

}
